/*!
 * \file ParserXTP.cpp
 * \project	WonderTrader
 *
 * \author Wesley
 * \date 2020/03/30
 * 
 * \brief XTP行情解析器实现文件
 * 
 * \details 该文件实现了ParserXTP类的所有功能，主要包括：
 *          - XTP行情API的完整封装和管理
 *          - 股票行情数据的实时接收和处理
 *          - 沪深两市股票合约的分别订阅管理
 *          - XTP连接状态监控和错误处理
 *          - 行情数据格式转换（XTP -> WonderTrader标准格式）
 *          - 多线程安全的数据处理机制
 *          - 动态库加载和API版本兼容性处理
 *          - 完整的日志记录和调试支持
 *          - 客户端参数配置和连接管理
 *          - 股票Level2行情数据的详细处理
 * 
 * \note 该实现专门针对XTP（中泰证券交易平台）系统的股票行情接入
 * \warning 使用前必须确保XTP动态库正确部署和配置
 */
#include "ParserXTP.h"

#include "../Includes/WTSDataDef.hpp"
#include "../Includes/WTSContractInfo.hpp"
#include "../Includes/WTSVariant.hpp"
#include "../Includes/IBaseDataMgr.h"

#include "../Share/TimeUtils.hpp"
#include "../Share/ModuleHelper.hpp"

#include <boost/filesystem.hpp>

/*!
 * \brief 通用日志记录模板函数
 * \tparam Args 可变参数类型
 * \param sink 行情回调接口指针
 * \param ll 日志级别
 * \param format 格式字符串
 * \param args 可变参数列表
 * 
 * \details 提供格式化日志输出功能，支持：
 *          - 多种日志级别（INFO、ERROR、WARN、DEBUG）
 *          - 线程安全的格式化输出
 *          - 自动内存管理和缓冲区保护
 *          - 与WonderTrader日志系统集成
 * 
 * \note 使用fmtlib提供高性能格式化，线程本地存储确保安全性
 */
 //By Wesley @ 2022.01.05
#include "../Share/fmtlib.h"
template<typename... Args>
inline void write_log(IParserSpi* sink, WTSLogLevel ll, const char* format, const Args&... args)
{
	if (sink == NULL)
		return;

	static thread_local char buffer[512] = { 0 };
	memset(buffer, 0, 512);
	fmt::format_to(buffer, format, args...);

	sink->handleParserLog(ll, buffer);
}

/*!
 * \brief C接口导出函数
 * 
 * 提供标准的动态库导出接口，支持插件化架构
 */
extern "C"
{
	/*!
	 * \brief 创建XTP行情解析器实例
	 * \return 新创建的行情解析器接口指针
	 * 
	 * 用于动态库插件系统，创建ParserXTP实例
	 */
	EXPORT_FLAG IParserApi* createParser()
	{
		ParserXTP* parser = new ParserXTP();
		return parser;
	}

	/*!
	 * \brief 销毁XTP行情解析器实例
	 * \param parser 要销毁的行情解析器接口指针（引用传递，销毁后置为NULL）
	 * 
	 * 安全地销毁ParserXTP实例并清理资源
	 */
	EXPORT_FLAG void deleteParser(IParserApi* &parser)
	{
		if (NULL != parser)
		{
			delete parser;
			parser = NULL;
		}
	}
};

/*!
 * \brief 时间字符串转换为整数时间
 * \param strTime 时间字符串（格式：HH:MM:SS）
 * \return 转换后的整数时间（格式：HHMMSS）
 * 
 * \details 将XTP API返回的时间字符串转换为WonderTrader使用的整数格式：
 *          - 移除时间分隔符（冒号）
 *          - 转换为HHMMSS格式的整数
 *          - 支持24小时制时间格式
 * 
 * \note 用于处理XTP返回的交易日期时间数据
 */
inline uint32_t strToTime(const char* strTime)
{
	std::string str;
	const char *pos = strTime;
	while(strlen(pos) > 0)
	{
		if(pos[0] != ':')
		{
			str.append(pos, 1);
		}
		pos++;
	}

	return strtoul(str.c_str(), NULL, 10);
}

/*!
 * \brief 检查浮点数有效性
 * \param val 要检查的浮点数值
 * \return 有效的浮点数值（无效时返回0）
 * 
 * \details 处理XTP API返回的可能无效的浮点数：
 *          - 检查是否为DBL_MAX或FLT_MAX（表示无效数据）
 *          - 无效数据转换为0
 *          - 确保数据的有效性和一致性
 * 
 * \note XTP行情中某些字段可能包含无效的最大值，需要过滤
 */
inline double checkValid(double val)
{
	if (val == DBL_MAX || val == FLT_MAX)
		return 0;

	return val;
}

/*!
 * \brief 构造函数
 * 
 * 初始化ParserXTP实例的各个成员变量为默认值
 */
ParserXTP::ParserXTP()
	:m_pUserAPI(NULL)
	,m_iRequestID(0)
	,m_uTradingDate(0)
{
}

/*!
 * \brief 析构函数
 * 
 * 清理ParserXTP实例，释放相关资源
 */
ParserXTP::~ParserXTP()
{
	m_pUserAPI = NULL;
}

/*!
 * \brief 初始化XTP行情解析器
 * \param config 配置参数对象
 * \return 是否初始化成功
 * 
 * \details 从配置文件中读取XTP连接参数并初始化API：
 *          - 服务器连接参数：host(主机地址)、port(端口号)
 *          - 用户认证信息：user(用户名)、pass(密码)
 *          - 协议配置：protocol(传输协议类型TCP/UDP)
 *          - 客户端配置：clientid(客户端ID)、hbinterval(心跳间隔)、buffsize(缓冲区大小)
 *          - 文件路径：flowdir(流文件目录)、xtpmodule(XTP动态库模块名)
 *          - 动态库加载：根据平台加载对应的XTP API动态库
 *          - API初始化：创建QuoteApi实例并注册回调接口
 *          - 目录创建：自动创建流文件存储目录
 * 
 * \note 默认流文件目录为"XTPMDFlow"，默认模块名为"xtpquoteapi"
 * \warning 必须确保XTP动态库文件存在并且版本兼容
 */
bool ParserXTP::init(WTSVariant* config)
{
	m_strHost	= config->getCString("host");
	m_iPort		= config->getInt32("port");
	m_strUser = config->getCString("user");
	m_strPass = config->getCString("pass");
	m_iProtocol = (XTP_PROTOCOL_TYPE)config->getUInt32("protocol");
	m_uClientID = config->getUInt32("clientid");
	m_uHBInterval = config->getUInt32("hbinterval");
	m_uBuffSize = config->getUInt32("buffsize");
	m_strFlowDir = config->getCString("flowdir");

	if (m_strFlowDir.empty())
		m_strFlowDir = "XTPMDFlow";

	m_strFlowDir = StrUtil::standardisePath(m_strFlowDir);

	std::string module = config->getCString("xtpmodule");
	if (module.empty())
		module = "xtpquoteapi";

	std::string path = StrUtil::printf("%s/%s/", m_strFlowDir.c_str(), m_strUser.c_str());
	boost::filesystem::create_directories(path.c_str());

	std::string dllpath = getBinDir() + DLLHelper::wrap_module(module.c_str(), "lib");;
	m_hInst = DLLHelper::load_library(dllpath.c_str());
#ifdef _WIN32
#	ifdef _WIN64
	const char* creatorName = "?CreateQuoteApi@QuoteApi@API@XTP@@SAPEAV123@EPEBDW4XTP_LOG_LEVEL@@@Z";
#	else
	const char* creatorName = "?CreateQuoteApi@QuoteApi@API@XTP@@SAPAV123@EPBDW4XTP_LOG_LEVEL@@@Z";
#	endif
#else
	const char* creatorName = "_ZN3XTP3API8QuoteApi14CreateQuoteApiEhPKc13XTP_LOG_LEVEL";
#endif
	m_funcCreator = (XTPCreater)DLLHelper::get_symbol(m_hInst, creatorName);
	m_pUserAPI = m_funcCreator(m_uClientID, path.c_str(), XTP_LOG_LEVEL_DEBUG);
	m_pUserAPI->RegisterSpi(this);

	return true;
}

/*!
 * \brief 释放资源
 * 
 * \details 释放XTP行情解析器占用的所有资源：
 *          - 断开与服务器的连接
 *          - 清理XTP API资源
 *          - 重置所有状态变量
 * 
 * \note 通过调用disconnect()实现资源清理
 */
void ParserXTP::release()
{
	disconnect();
}

/*!
 * \brief 建立连接
 * \return 是否连接成功
 * 
 * \details 启动XTP行情API连接流程：
 *          - 调用DoLogin()执行登录操作
 *          - 登录成功后自动开始行情数据订阅
 * 
 * \note 实际的连接建立在DoLogin()中完成
 */
bool ParserXTP::connect()
{
	DoLogin();

	return true;
}

/*!
 * \brief 断开连接
 * \return 是否断开成功
 * 
 * \details 安全断开与XTP行情服务器的连接：
 *          - 注销事件回调接口(RegisterSpi(NULL))
 *          - 释放XTP API资源(Release())
 *          - 重置API指针为NULL
 * 
 * \note 确保先注销回调再释放API，避免回调异常
 */
bool ParserXTP::disconnect()
{
	if(m_pUserAPI)
	{
		m_pUserAPI->RegisterSpi(NULL);
		m_pUserAPI->Release();
		m_pUserAPI = NULL;
	}

	return true;
}

/*!
 * \brief XTP API错误信息处理
 * \param error_info XTP错误信息结构体指针
 * 
 * \details 处理XTP API返回的错误信息：
 *          - 调用IsErrorRspInfo()检查错误详情
 *          - 记录错误信息用于调试和监控
 * 
 * \note 统一的错误处理入口点
 */
void ParserXTP::OnError(XTPRI *error_info)
{
	IsErrorRspInfo(error_info);
}

/*!
 * \brief 连接断开事件处理
 * \param nReason 断开原因代码
 * 
 * \details 处理与XTP行情服务器的连接断开事件：
 *          - 记录断开原因和错误信息
 *          - 通知上层应用连接状态变化
 *          - 触发WPE_Close事件进行连接恢复处理
 * 
 * \note XTP API会自动进行重连尝试，客户端无需手动重连
 */
void ParserXTP::OnDisconnected(int nReason)
{
	if(m_sink)
	{
		write_log(m_sink, LL_ERROR, "[ParserXTP] Market data server disconnected: {}...", nReason);
		m_sink->handleEvent(WPE_Close, 0);
	}
}

/*!
 * \brief 退订行情响应处理
 * \param ticker 退订的股票合约信息
 * \param error_info 错误信息（为空表示成功）
 * \param is_last 是否为最后一个响应
 * 
 * \details 处理股票行情退订操作的响应，当前实现为空，可根据需要扩展
 * 
 * \note 预留接口，用于处理退订响应和错误处理
 */
void ParserXTP::OnUnSubMarketData(XTPST *ticker, XTPRI *error_info, bool is_last)
{

}

/*!
 * \brief 深度行情数据处理
 * \param market_data XTP行情数据结构
 * \param bid1_qty 买一队列数据数组
 * \param bid1_count 买一队列有效委托笔数
 * \param max_bid1_count 买一队列总委托笔数
 * \param ask1_qty 卖一队列数据数组
 * \param ask1_count 卖一队列有效委托笔数
 * \param max_ask1_count 卖一队列总委托笔数
 * 
 * \details 这是最核心的股票行情数据处理方法，功能包括：
 *          - 股票合约信息验证：检查合约是否存在于基础数据中
 *          - 交易所识别：区分上海证券交易所(SSE)和深圳证券交易所(SZSE)
 *          - 时间处理：解析XTP时间戳为日期和时间
 *          - 数据转换：将XTP行情数据转换为WonderTrader标准tick格式
 *          - 价格校验：使用checkValid确保价格数据有效性
 *          - 成交量处理：处理成交量和成交额数据
 *          - 盘口数据：处理10档买卖盘口价格和数量
 *          - 特殊字段：处理期货特有的结算价、持仓量等字段
 *          - 涨跌停处理：处理涨停价和跌停价
 *          - 队列数据：支持Level2买卖队列详细数据（预留功能）
 * 
 * \note 支持股票和期货不同品种的数据处理，自动适配字段差异
 * \warning 必须确保m_pBaseDataMgr有效，否则无法处理行情数据
 */
void ParserXTP::OnDepthMarketData(XTPMD *market_data, int64_t bid1_qty[], int32_t bid1_count, int32_t max_bid1_count, int64_t ask1_qty[], int32_t ask1_count, int32_t max_ask1_count)
{	
	if(m_pBaseDataMgr == NULL)
	{
		return;
	}

	uint32_t actDate = (uint32_t)(market_data->data_time / 1000000000);
	uint32_t actTime = market_data->data_time % 1000000000;
	uint32_t actHour = actTime / 10000000;

	std::string code, exchg;
	if (market_data->exchange_id == XTP_EXCHANGE_SH)
	{
		exchg = "SSE";
	}
	else
	{
		exchg = "SZSE";
	}
	code = market_data->ticker;

	WTSContractInfo* ct = m_pBaseDataMgr->getContract(code.c_str(), exchg.c_str());
	if(ct == NULL)
	{
		if (m_sink)
			write_log(m_sink, LL_ERROR, "[ParserXTP] Instrument {}.{} not exists...", exchg.c_str(), market_data->ticker);
		return;
	}
	WTSCommodityInfo* commInfo = ct->getCommInfo();

	WTSTickData* tick = WTSTickData::create(code.c_str());
	tick->setContractInfo(ct);
	WTSTickStruct& quote = tick->getTickStruct();
	strcpy(quote.exchg, commInfo->getExchg());
	
	quote.action_date = actDate;
	quote.action_time = actTime;
	
	quote.price = checkValid(market_data->last_price);
	quote.open = checkValid(market_data->open_price);
	quote.high = checkValid(market_data->high_price);
	quote.low = checkValid(market_data->low_price);
	quote.total_volume = (uint32_t)market_data->qty;
	quote.trading_date = m_uTradingDate;
	quote.total_turnover = market_data->turnover;

	if (commInfo->getCategoty() == CC_Future)
	{
		quote.settle_price = market_data->settl_price;
		quote.open_interest = (uint32_t)market_data->total_long_positon;

		quote.pre_settle = checkValid(market_data->pre_settl_price);
		quote.pre_interest = (uint32_t)market_data->pre_total_long_positon;
	}

	quote.upper_limit = checkValid(market_data->upper_limit_price);
	quote.lower_limit = checkValid(market_data->lower_limit_price);

	quote.pre_close = checkValid(market_data->pre_close_price);	

	// 十档委托价格和数量处理
	for (int i = 0; i < 10; i++)
	{
		quote.ask_prices[i] = checkValid(market_data->ask[i]);
		quote.ask_qty[i] = (uint32_t)market_data->ask_qty[i];

		quote.bid_prices[i] = checkValid(market_data->bid[i]);
		quote.bid_qty[i] = (uint32_t)market_data->bid_qty[i];
	}

	if(m_sink)
		m_sink->handleQuote(tick, 1);

	tick->release();
}

/*!
 * \brief 订阅行情响应处理
 * \param ticker 订阅的股票合约信息
 * \param error_info 错误信息（为空表示成功）
 * \param is_last 是否为最后一个响应
 * 
 * \details 处理股票行情订阅操作的响应：
 *          - 检查订阅是否成功
 *          - 记录订阅失败的错误信息
 *          - 区分上海和深圳交易所的订阅结果
 * 
 * \note 用于监控行情订阅状态，便于调试和问题定位
 */
void ParserXTP::OnSubMarketData(XTPST *ticker, XTPRI *error_info, bool is_last)
{
	if (!IsErrorRspInfo(error_info))
	{

	}
	else
	{
		if(m_sink)
			write_log(m_sink, LL_ERROR, "[ParserXTP] Market data subscribe faile, code: {}.{}", ticker->exchange_id == XTP_EXCHANGE_SH ? "SSE" : "SZSE", ticker->ticker);
	}
}

/*!
 * \brief 执行登录操作
 * 
 * \details 向XTP服务器发起登录请求，包括：
 *          - 使用配置的主机地址、端口、用户名、密码进行登录
 *          - 指定传输协议类型（TCP/UDP）
 *          - 处理登录结果和错误情况
 *          - 登录成功后获取交易日期
 *          - 触发连接和登录事件通知
 *          - 自动执行行情数据订阅
 * 
 * \note 登录成功后会自动调用DoSubscribeMD()进行行情订阅
 * \warning 必须确保网络连接正常和账户信息正确
 */
void ParserXTP::DoLogin()
{
	if(m_pUserAPI == NULL)
	{
		return;
	}

	int iResult = m_pUserAPI->Login(m_strHost.c_str(), m_iPort, m_strUser.c_str(), m_strPass.c_str(), m_iProtocol);
	if(iResult != 0)
	{
		if (m_sink)
		{
			if(iResult == -1)
			{
				m_sink->handleEvent(WPE_Connect, iResult);
			}
			else
			{
				m_sink->handleEvent(WPE_Connect, 0);

				write_log(m_sink, LL_ERROR, "[ParserXTP] Sending login request failed: {}", iResult);
			}
			
		}
	}
	else
	{
		m_uTradingDate = strToTime(m_pUserAPI->GetTradingDay());
		if (m_sink)
		{
			m_sink->handleEvent(WPE_Connect, 0);
			m_sink->handleEvent(WPE_Login, 0);
		}

		DoSubscribeMD();
	}
}

/*!
 * \brief 执行行情数据订阅
 * 
 * \details 分别处理上海和深圳交易所的行情订阅：
 *          - 上海交易所(SSE)：处理m_fitSHSubs中的股票代码
 *          - 深圳交易所(SZSE)：处理m_fitSZSubs中的股票代码
 *          - 批量订阅：将股票代码转换为字符指针数组
 *          - 错误处理：记录订阅失败的详细信息
 *          - 成功确认：记录成功订阅的股票数量
 *          - 内存管理：正确释放临时分配的内存
 * 
 * \note 支持全市场订阅（注释掉的SubscribeAllMarketData）
 * \warning 必须在登录成功后调用，确保订阅权限有效
 */
void ParserXTP::DoSubscribeMD()
{
	CodeSet codeFilter = m_fitSHSubs;
	if(!codeFilter.empty())
	{
		char ** subscribe = new char*[codeFilter.size()];
		int nCount = 0;
		CodeSet::iterator it = codeFilter.begin();
		for (; it != codeFilter.end(); it++)
		{
			subscribe[nCount++] = (char*)(*it).c_str();
		}

		if (m_pUserAPI && nCount > 0)
		{
			int iResult = m_pUserAPI->SubscribeMarketData(subscribe, nCount, XTP_EXCHANGE_SH);
			if (iResult != 0)
			{
				if (m_sink)
					write_log(m_sink, LL_ERROR, "[ParserXTP] Sending md subscribe request of SSE failed: {}", iResult);
			}
			else
			{
				if (m_sink)
					write_log(m_sink, LL_INFO, "[ParserXTP] Market data of {} instruments of SSE subscribed", nCount);
			}
		}
		codeFilter.clear();
		delete[] subscribe;
		//int iResult = m_pUserAPI->SubscribeAllMarketData(XTP_EXCHANGE_SH);
	}

	codeFilter = m_fitSZSubs;
	if (!codeFilter.empty())
	{
		char ** subscribe = new char*[codeFilter.size()];
		int nCount = 0;
		CodeSet::iterator it = codeFilter.begin();
		for (; it != codeFilter.end(); it++)
		{
			subscribe[nCount++] = (char*)(*it).c_str();
		}

		if (m_pUserAPI && nCount > 0)
		{
			int iResult = m_pUserAPI->SubscribeMarketData(subscribe, nCount, XTP_EXCHANGE_SZ);
			if (iResult != 0)
			{
				if (m_sink)
					write_log(m_sink, LL_ERROR, "[ParserXTP] Sending md subscribe request of SZSE failed: {}", iResult);
			}
			else
			{
				if (m_sink)
					write_log(m_sink, LL_INFO, "[ParserXTP] Market data of {} instruments of SZSE subscribed", nCount);
			}
		}
		codeFilter.clear();
		delete[] subscribe;
		//int iResult = m_pUserAPI->SubscribeAllMarketData(XTP_EXCHANGE_SZ);
	}
}

/*!
 * \brief 检查响应信息是否包含错误
 * \param error_info XTP错误信息结构体指针
 * \return true表示有错误，false表示无错误
 * 
 * \details 检查XTP API返回的响应信息：
 *          - NULL指针检查：error_info为空表示无错误
 *          - 错误代码检查：error_id为0表示操作成功
 *          - 返回布尔值便于调用方判断
 * 
 * \note 这是XTP API错误处理的标准模式
 */
bool ParserXTP::IsErrorRspInfo(XTPRI *error_info)
{
	if (error_info == NULL || error_info->error_id ==0)
		return false;

	return true;
}

/*!
 * \brief 订阅股票行情数据
 * \param vecSymbols 要订阅的股票代码集合
 * 
 * \details 处理股票行情订阅请求，功能包括：
 *          - 交易所识别：自动识别SSE和SZSE前缀
 *          - 代码处理：去除交易所前缀，保留纯股票代码
 *          - 分类管理：分别加入上海和深圳订阅列表
 *          - 即时订阅：如果已登录，立即发送订阅请求
 *          - 延迟订阅：如果未登录，保存到待订阅列表
 *          - 批量处理：支持同时订阅多个股票代码
 *          - 内存管理：正确管理临时分配的字符指针数组
 * 
 * \note 支持SSE.xxxxxx和SZSE.xxxxxx格式的股票代码
 * \warning 必须确保股票代码格式正确，否则可能订阅失败
 */
void ParserXTP::subscribe(const CodeSet &vecSymbols)
{
	if(m_uTradingDate == 0)
	{
		for(auto& code : vecSymbols)
		{
			if (strncmp(code.c_str(), "SSE.", 4) == 0)
			{
				m_fitSHSubs.insert(code.c_str() + 4);
			}
			else if (strncmp(code.c_str(), "SZSE.", 5) == 0)
			{
				m_fitSZSubs.insert(code.c_str() + 5);
			}
		}
	}
	else
	{
		CodeSet setSH, setSZ;
		for (auto& code : vecSymbols)
		{
			if (strncmp(code.c_str(), "SSE.", 4) == 0)
			{
				m_fitSHSubs.insert(code.c_str() + 4);
				setSH.insert(code.c_str() + 4);
			}
			else if (strncmp(code.c_str(), "SZSE.", 5) == 0)
			{
				m_fitSZSubs.insert(code.c_str() + 5);
				setSZ.insert(code.c_str() + 5);
			}
		}

		if (!setSH.empty())
		{
			char ** subscribe = new char*[setSH.size()];
			int nCount = 0;
			CodeSet::iterator it = setSH.begin();
			for (; it != setSH.end(); it++)
			{
				subscribe[nCount++] = (char*)(*it).c_str();
			}

			if (m_pUserAPI && nCount > 0)
			{
				int iResult = m_pUserAPI->SubscribeMarketData(subscribe, nCount, XTP_EXCHANGE_SH);
				if (iResult != 0)
				{
					if (m_sink)
						write_log(m_sink, LL_ERROR, "[ParserXTP] Sending md subscribe request of SSE failed: {}", iResult);
				}
				else
				{
					if (m_sink)
						write_log(m_sink, LL_INFO, "[ParserXTP] Market data of {} instruments of SSE subscribed", nCount);
				}
			}
			delete[] subscribe;
		}

		if (!setSZ.empty())
		{
			char ** subscribe = new char*[setSZ.size()];
			int nCount = 0;
			CodeSet::iterator it = setSZ.begin();
			for (; it != setSZ.end(); it++)
			{
				subscribe[nCount++] = (char*)(*it).c_str();
			}

			if (m_pUserAPI && nCount > 0)
			{
				int iResult = m_pUserAPI->SubscribeMarketData(subscribe, nCount, XTP_EXCHANGE_SZ);
				if (iResult != 0)
				{
					if (m_sink)
						write_log(m_sink, LL_ERROR, "[ParserXTP] Sending md subscribe request of SZSE failed: {}", iResult);
				}
				else
				{
					if (m_sink)
						write_log(m_sink, LL_INFO, "[ParserXTP] Market data of {} instruments of SZSE subscribed", nCount);
				}
			}
			delete[] subscribe;
		}
	}
}

/*!
 * \brief 退订股票行情数据
 * \param vecSymbols 要退订的股票代码集合
 * 
 * \details 处理股票行情退订请求，当前实现为空，可根据需要扩展
 * 
 * \note 预留接口，用于实现行情数据的动态退订功能
 */
void ParserXTP::unsubscribe(const CodeSet &vecSymbols)
{
}

/*!
 * \brief 检查连接状态
 * \return true表示已连接，false表示未连接
 * 
 * \details 通过检查XTP API指针是否有效来判断连接状态
 * 
 * \note 简单有效的连接状态检查方法
 */
bool ParserXTP::isConnected()
{
	return m_pUserAPI!=NULL;
}

/*!
 * \brief 注册行情数据回调接口
 * \param listener 行情数据处理回调接口指针
 * 
 * \details 注册行情数据处理接口，同时获取基础数据管理器：
 *          - 保存回调接口指针用于数据推送
 *          - 获取基础数据管理器用于合约信息查询
 *          - 确保行情数据处理的完整性
 * 
 * \note 基础数据管理器用于验证股票合约信息的有效性
 */
void ParserXTP::registerSpi(IParserSpi* listener)
{
	m_sink = listener;

	if(m_sink)
		m_pBaseDataMgr = m_sink->getBaseDataMgr();
}